
package horde.internalResources;

// <editor-fold defaultstate="collapsed" desc="Imports">
import com.sun.awt.AWTUtilities;
import horde.comunications.RMIDock;
import horde.patterns.Agent;
import horde.patterns.master.MasterAgent;
import horde.internalResources.HorrorControl.HorrorControl;
import horde.internalResources.dictionaries.dictionary;
import horde.utils.ParseXML;
import horde.utils.ParseXML.Node;
import horde.utils.RawLoader;
import horde.utils.Toolbox;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.LayoutManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Properties;
import javax.swing.Timer;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextPane;
import javax.swing.OverlayLayout;
// </editor-fold>

/**
 * Launch screen and logic to instance a node from xml
 * @author Andres Picazo
 * @author Arturo Mazon
 */
public class Launcher extends JFrame {

    // <editor-fold defaultstate="collapsed" desc="Constructor">
    /**
     * Creates and displays the launcher screen
     * @param title Title to display into the screen
     * @param imageLocation Path to find the image to be displayed into the background
     * @param textColor Color for the text displayed while executing
     * @param topGap Gap to preserve before text is displayed
     */
    public Launcher (String title, String imageLocation, Color textColor, int topGap) {
        super();
        /* Just to see what does this thing does, ignore next line */
//        AWTUtilities.setWindowOpacity(this, 0.5f);
        setTitle(title);
        image = new RawLoader().getStandardImage(imageLocation);
        if (image != null) {
            imageCanvas = new JLabel("");
            imageCanvas.setIcon(image);
            background = new JPanel(new BorderLayout());
            background.add(imageCanvas, BorderLayout.CENTER);
            foreground = new JPanel(new BorderLayout());
            foreground.setOpaque(false);
            /* For testing only... */
            //foreground.add(new JLabel(title), BorderLayout.CENTER);
            setForegroundPanel(textColor, topGap);
            /* Container for the panels */
            container = new JPanel();
            container.setBorder(BorderFactory.createLineBorder(Color.white));
            view = new OverlayLayout(container);
            container.setLayout(view);
            container.add (foreground);
            container.add (background);
            setLayout(new BorderLayout());
            add (container, BorderLayout.CENTER);
            setUndecorated(true);
            setSize(defaultSize);

            setLocationRelativeTo(null);
            setVisible(true);
            /* Propietary code, gives many annoying warnings, commented */
//            Shape shape = new RoundRectangle2D.Float(0, 0, 400, 300, 25, 25);
//            AWTUtilities.setWindowShape(this,shape);
        }
        else JOptionPane.showMessageDialog(null, "Image failed to load: " + imageLocation);
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Launcher start">
    /**
     * Parses the xml manifest file located at "manifestPath", and returns the master agent
     * @param manifestPath Path to locate the manifest
     * @return Instanced master agent at charge of the node
     */
    public static MasterAgent start (String manifestPath) {
        rmi = RMIDock.getInstance();
        parseXML(manifestPath);
        // <editor-fold defaultstate="collapsed" desc="Init launcher and set locale">
        String launcher_title = appProperties.getProperty("launcherTitle");
        String launcher_image = appProperties.getProperty("launcherImagePath");
        int launcher_headroom = 210;
        if (appProperties.getProperty("launcherHeadroom") != null) {
            try {
                launcher_headroom = Integer.parseInt(appProperties.getProperty("launcherHeadroom"));
            } catch (Exception e) {
               launcher_headroom = 210;
            }
        }
        /* Set the language */
        String locale = appProperties.getProperty("language");
        if (locale == null) locale = "EN";
        dictionary.setBundle(locale);
        /* Language done */
        if (launcher_title == null) launcher_title = dictionary.getString("launcher.title");
        if (launcher_image == null) launcher_image = RawLoader.hordeSplash;
        Launcher L = new Launcher(launcher_title,launcher_image, Color.red, launcher_headroom);
        // </editor-fold>
        L.showMessage("\nCreating agents...\n");
        MasterAgent master = instanceAgents(L);
        // <editor-fold defaultstate="collapsed" desc="Shared resources">
        if (shared != null) {
            for (Node z : shared) {
                String id = z.getProperty("id");
                String path = z.getProperty("path");
                try {
                    Class resourceClass = Class.forName(path);
                    Object resource = resourceClass.newInstance();
                    master.addSharedResource(id, path, resource);
                }
                catch (Exception e) {
                    System.err.println ("Can't instance resource: " + path);
                }
            }
        }
        // </editor-fold>
        /* Animation */
        try {Thread.sleep(2000);} catch (Exception e) {}
        L.fadeAndDispose();
        return master;
    }

    /**
     * Parses the xml file into the nodes array and gets the data from it
     * @param manifestPath Path to the xml file
     */
    private static void parseXML (String manifestPath) {
        Node parsedNode = ParseXML.parseFile(manifestPath);
        ArrayList<Node> nodes = parsedNode.getChildNodes();
        for (Node n : nodes) {
            if (n.getName().toLowerCase().equals("properties")) {
                appProperties = n.getProperties();
            }
            if (n.getName().toLowerCase().equals("routes")) {
                routeTable = n.getProperties();
                String localIP = (new Toolbox()).getIP();
//                if (!routeTable.containsKey("local"))
                routeTable.setProperty("local", localIP);
            }
            /* Read the agent models */
            else if (n.getName().toLowerCase().equals("roles")) {
                ArrayList<Node> models = n.getChildNodes();
                for (Node k : models) {
                    agentModels.put(k.getProperty("name"), k);
                }
            }
            /* The agent instances */
            else if (n.getName().toLowerCase().equals("agent_instances")) {
                instances = n.getChildNodes();
            }
            /* The shared resources */
            else if (n.getName().toLowerCase().equals("shared")) {
                shared = n.getChildNodes();
            }
        }
    }

    private static MasterAgent instanceAgents (Launcher L) {
        // <editor-fold defaultstate="collapsed" desc="master creation and config">
        String masterName = appProperties.getProperty("masterID");
        if (masterName == null) masterName = "MASTER";
        MasterAgent master = null;
        try {
            master = new MasterAgent(masterName, appProperties);
        } catch (Exception e) {
        }

        L.showMessage("Created master agent: " + masterName);
//        master.setProperties(appProperties);
        master.setRoutes(routeTable);
        // </editor-fold>
        for (int agentCount = 0; agentCount < instances.size(); agentCount++) {
            L.showMessage("Creating agent " + (agentCount+1) + " of " + instances.size());
            Properties agentProperties = instances.get(agentCount).getProperties();
            String node = agentProperties.getProperty("node");
            // <editor-fold defaultstate="collapsed" desc="Deployment filter">
            boolean instanceAgent = true;
            if (node == null || node.equals("")) {
                agentProperties.setProperty("node", "local");
                node = "local";
            }
            else if (!node.equals("local")) {
                /* Checks if this is the agent deploy zone */
                String localIP = (new Toolbox()).getIP();
                String deployZone = routeTable.getProperty(node);
                if (!localIP.equals(deployZone)) instanceAgent = false;
            }
            // </editor-fold>
            /* ++++++++++++++++++++++++ */
            String name = agentProperties.getProperty("id");
            String role = agentProperties.getProperty("role");
            String group = agentProperties.getProperty("group");
            /* Sets the RMI route table */
            rmi.addRoute(name, node, routeTable.getProperty(node));
            L.showMessage("New route for agent " + name + " at " + node + ":" + routeTable.getProperty(node));
            if (group == null || group.equals("")) {
                agentProperties.setProperty("group", "default");
                group = "default";
            }
            // Add to the directory
            Directory.getInstance().addGroupMember(name, group);
            L.showMessage("Added agent " + name + " to group " + group);
            /* ++++++++++++++++++++++++ */
            /* Only instance the agents to be deployed in this node */
            // <editor-fold defaultstate="collapsed" desc="Create agent instances">
            if (instanceAgent) {                
                Node model = (Node)agentModels.get(role);
                String behaviorPath = model.getProperty("behavior");
                String corePath = model.getProperty("core");
                Agent agent = new Agent(name,behaviorPath,corePath);
                L.showMessage("Created agent " + name + " with role " + role);
                master.addAgent(agent, agentProperties);
            } else L.showMessage("Skipping: " + agentProperties.getProperty("id"));
            // </editor-fold>
        }
        return master;
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Graphics">

    /**
     * Creates the progress bar and the text area for the process display
     * @param textColor Color for the displayed text
     * @param topGap Gap to reserve before the text starts to appear
     */
    private void setForegroundPanel (Color textColor, int topGap) {
        /* Setting opacity */
        foreground.setOpaque(false);
//        foreground.setBackground(new Color(0, 0, 0, 64));
        /* Adds an indeterminate progress bar, does nothing, but looks cool XD */
        progressBar = new JProgressBar();
        progressBar.setIndeterminate(true);
        foreground.add(progressBar, BorderLayout.PAGE_END);
        /* Text area */
        text = new JTextPane();
        Font font = new Font("Serif", Font.BOLD, 12);
        text.setFont(font);
        text.setOpaque(false);
        text.setForeground(textColor);
        textContainer = new JScrollPane(text);
        textContainer.setOpaque(false);
        textContainer.getViewport().setOpaque(false);
        text.setEditable(false);
        text.setFocusable(false);
        
        textContainer.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER);
        textContainer.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);

        text.setText(text.getText()+"Processing...");
        JLabel space = new JLabel(" ");
        space.setPreferredSize(new Dimension(400,topGap));
        foreground.add(space, BorderLayout.NORTH);
        foreground.add(textContainer, BorderLayout.CENTER);        
    }

    /**
     * Shows a new message in the panel text area and moves the caret to show the last messages
     * @param t Text message to display (into a new line, adds "\n" automatically)
     */
    public void showMessage (String t) {
        text.setText(text.getText()+"\n"+t);
        text.setCaretPosition(text.getDocument().getLength()-1);
    }

    /* Returns a boolean to allow calling method to wait for the fading to finish */
    /**
     * Implements the fading effect and disposes the launcher window.
     * @return Allways returns true, to allow calling object to wait until the fading effect as finished.
     */
    public boolean fadeAndDispose () {
        progressBar.setIndeterminate(false);
        /* Points to the launcher, to include it into the actionPerformed */
        selfPointer = this;
        timer = new Timer (75, new ActionListener () {
            public void actionPerformed(ActionEvent e)
            {
                float currentAlpha = AWTUtilities.getWindowOpacity(selfPointer) - 0.1f;
                if (currentAlpha <= 0f) {
                    AWTUtilities.setWindowOpacity(selfPointer, 0f);
                    stopTimerAndDispose();
                }
                else AWTUtilities.setWindowOpacity(selfPointer, currentAlpha);
            }
        });
        timer.setRepeats(true);
        timer.start();
        return true;
    }

    /**
     * Stops the timer for the fadeAndDispose method and disposes of the frame
     */
    private void stopTimerAndDispose () {
        timer.stop();
        dispose();
    }
    // </editor-fold>

    // <editor-fold defaultstate="collapsed" desc="Attributes">

    /* Attributes */
    /**
     * Manager to implement the overlay layout
     */
    private LayoutManager view;
    private JPanel container;
    /**
     * JPanel for the background (contains the imageCanvas)
     */
    private JPanel background;
    /**
     * Contains the text JTextPane to display the messages
     */
    private JPanel foreground;
    /**
     * Contains the image to display into the background
     */
    private JLabel imageCanvas;
    private ImageIcon image;
    /**
     * Set size for the frame
     */
    private Dimension defaultSize = new Dimension (400,300);
    private JProgressBar progressBar;
    private JScrollPane textContainer;
    /**
     * Displays the text for the messages
     */
    private JTextPane text;
    private JFrame selfPointer;
    private Timer timer;
    private static HorrorControl hc;


    /**
     * Variables for the parsing
     */
    private static ArrayList<Node> instances = null;
    private static ArrayList<Node> shared = null;
    private static Properties appProperties = null;
    private static Properties routeTable = null;
    private static HashMap agentModels = new HashMap();
    private static RMIDock rmi;
    // </editor-fold>
}
